React - 基础和TodoList

React基础

React环境

下载安装or更新node

更新node和npm至新版(要求:Node >= 6 and npm >= 5.2)

image-20190415193156127

创建 React App

1
2
3
npx create-react-app my-app
cd my-app
npm start

image-20190415194142203

image-20190415194606970

项目代码精简

项目入口:index.js

内容写在:APP.js

image-20190415195838847

什么是组件

网页整个是一个组件,各个部分也是一个组件。

只要是页面上的一部分,就是一个组件。eg.网页上的标签

image-20190415195929586

index.js

1
2
3
// react:让浏览器理解<App />组件的语法
// react-dom:加载组件,将组件渲染到dom节点上(标签内)。将APP渲染到index.html标签里去
// App.js:APP(最外层的)组件,大写字母开头都是组件

App.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import React from 'react';

// 定义一个React组件:App是一个类,继承Component类
class App extends React.Component {
// 必须需要的函数,负责组件显示的内容:return的内容为需要显示的内容。
render() {
return (
<div>
hello iris
</div>
);
}
}
// 导出出去,index.js才可以引入
export default App;

JSX语法

允许在react中,直接使用标签<>结构

注释:{/ /}

class样式为:className,否则会被认为是组件类的class

label的for标签为:htmlFor,否则会被认为是for循环关键字

实战TodoList

React 面向数据编程,不实际操作Dom

数据

构造函数中控制数据

1
2
3
4
5
6
7
8
9
// Todolist组件的构造函数,组件创造的瞬间,自动执行。
constructor(props) {
super(props);
// 创建了state数据项:数据存放的位置
this.state = {
list: [],
inputValue: ''
}
}

展示列表

1
2
3
4
5
6
7
8
9
<ul>
{
// 循环list中的数据,每循环一次,返回一个类标签
this.state.list.map((item, index) => {
// 类标签需要唯一key值
return <li key={index} onClick={this.handleItemClick.bind(this, index)}>{item}</li>
})
}
</ul>

新增列表

  • input输入内容后:将输入框内容的值初始化为inputValue,其改变的时候自动更新inputValue中
  • button点击后:将inputValue加载在list中最后一位,清空inputValue。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 1 添加任务
// 1.1输入框改变,保存输入框内容
handleInputChange(e) {
console.log(e.target.value)
this.setState({
inputValue: [e.target.value]
})
}
// 1.2点击add,增加一项数据
handleBtnClick() {
// 按下按钮,调用setState来改变数据list:...展开运算符,表示老的内容 + 增加的新的内容
this.setState({
list: [...this.state.list, this.state.inputValue],
inputValue: ''
})
alert('click');
}

删除列表

数据项被点击后:

  • 获取index值,作为删除函数输入
  • 复制list于const list
  • 删除list中index的数据项
  • 在更新原list(不直接操作list),setState操作
1
2
3
4
5
6
7
8
9
10
11
12
// 2 删除任务
// 选择数据项,删除数据项:通过list的index获取其下标,再删除
handleItemClick(index) {
console.log(index)
// 拷贝、删除、复制
const list = [...this.state.list]
list.splice(index,1)
this.setState({
list: list
})
// this.setState({list}) ES6的新写法,键值一致的写法
}

其他

bind方法保持this上下文

写法一,直接写在HTML标签内:

1
<button onClick={this.handleBtnClick.bind(this)}>add</button>

若不绑定,则handleBtnClick()函数中的this则指的是button,而不是组件props本身。

写法二,写在构造函数中:提升代码执行性能(底层内容)

render()只能返回return一个包裹标签

1
2
3
4
5
6
7
8
9
render() {
// 返回一个大组件:1.一个div:root的div中会多一层div包裹 2.React.Fragment只有root的div
return (
<React.Fragment>
<div></div>
<ul></ul>
</React.Fragment>
);
}

✨组件通信

父组件向子组件传值:父组件的属性

父组件通过属性的方式向子组件传递参数

子组件通过props的形式接收父组件传递的参数

子组件向父组件传值:通过调用父组件的方法改变数据

CSS

行内样式 style

1
2
/* 外层{}表示为JS表达式;里面的{}表达式为JS的对象*/
style={{background:'red',color:'#fff'}}

className的方法(不能使用class关键字,react中的class关键字表示定义一个组件)

1
<button className='red-btn' onClick={this.handleBtnClick}>add</button>

步骤:关键字className,定义css文件,在入口处index.js引入css文件

###

代码优化

1
2
3
4
5
6
7
8
9
10
11
// setState性能优化的方式:函数的形式变为异步的setState
// 将target存储在外层,再在内部适用对象
const value = e.target.value;
this.setState(() => ({
inputValue: value
}));

// setState原本的方式:不推荐
this.setState({
inputValue: [e.target.value]
}
1
2
3
4
5
6
7
8
9
10
11
// 新方法:
// prevState:修改数据之前的那一次数据的状态;避免不小心改变state状态
this.setState((prevState) => ({
list: [...prevState.list, prevState.inputValue],
inputValue: ''
}));
// 原方法:
this.setState({
list: [...this.state.list, this.state.inputValue],
inputValue: ''
});

关于React的思考

声明式开发

减少大量dom操作代码量

可以和其他框架并存

react只去管理id=”root”的div的渲染。jQuery可以管理其他div操作。

组件化

  • 区分组件和标签
    xxx
  • 组件之间通信:
    • 父向子:属性
    • 子向父:调用父组件的方法

单向数据流

父组件可以向子组件传值(只读),子组件不能去改变这个值。

why单项数据流:防止其中一个改变时出现bug,难以定位是哪一个组件改变导致的错误。

视图层框架

只解决数据和渲染的问题,不解决react复杂传值问题,可以借助其他redux等数据传递框架

函数式编程

代码逻辑清晰:拆分函数,各司其职;

前端自动化测试便捷:只需要给函数一个数值,查看输出是否符合预期。